#include "CharsetCodeX.h"

long AtoW(char *ansi, wchar_t **unicode)
{
	int retVal = RTN_ERROR;
	
	size_t nSize = strlen(ansi);
	*unicode = new wchar_t[nSize+1];
	memset( *unicode, 0x00, (nSize + 1)*sizeof(wchar_t) );

#ifdef WIN32
	
	wchar_t* pszwOut = NULL;
	USES_CONVERSION;
	pszwOut = A2W(ansi);
	memcpy( *unicode, pszwOut, sizeof(wchar_t)*nSize );
	retVal = (int)nSize;

#else
	
	setlocale(LC_ALL, "zh_CN.gbk");
	retVal = mbstowcs( *unicode, ansi, nSize );
	if ( retVal == -1 )
	{
		delete *unicode;
		*unicode = NULL;	
	}	

#endif

	return retVal;	
}

long WtoA(wchar_t *unicode, char **ansi)
{
	int retVal = RTN_ERROR;
	
	size_t nSize = wcslen(unicode);
	*ansi = new char[nSize+1];
	memset( *ansi, 0x00, (nSize + 1)*sizeof(char) );

#ifdef WIN32
	
	char* pszOut = NULL;
	USES_CONVERSION;
	pszOut = W2A(unicode);
	memcpy( (void*)(*unicode), pszOut, sizeof(char)*nSize );
	retVal = (int)nSize;

#else

	setlocale( LC_ALL, "zh_CN.gbk" );
	retVal = wcstombs( *ansi, unicode, nSize );
	if ( retVal == -1 )
	{
		delete *ansi;
		*ansi = NULL;	
	}	

#endif
	
	return retVal;
}

namespace Charset_Coders
{
    bool Unicode2UTF8(const wchar_t *input_unicode,
                    char ** p_output_utf8,
                    unsigned long *length) {
        if (input_unicode == NULL) {
            return true;
        }
        int size_d = 8;
        int buffer_size = 0;
        
        const wchar_t* p_unicode = input_unicode;
        // count for the space need to allocate
        wchar_t w_char;
        do {
            w_char = *p_unicode;
            if (w_char < 0x80) {
                // utf char size is 1
                buffer_size += 1;
            } else if (w_char < 0x800) {
                // utf char size is 2
                buffer_size += 2;
            } else if (w_char < 0x10000) {
                // utf char size is 3
                buffer_size += 3;
            } else if (w_char < 0x200000) {
                // utf char size is 4
                buffer_size += 4;
            } else if (w_char < 0x4000000) {
                // utf char size is 5
                buffer_size += 5;
            } else {
                // utf char size is 6
                buffer_size += 6;
            }
            p_unicode++;
        }
        while (w_char != static_cast<char>(0));
        // allocate the memory
        char* utf8 = new char[buffer_size];
        
        p_unicode = input_unicode;
        int index_buffer = 0;
        // do the conversion
        do {
            w_char = *input_unicode;  // the unicode char current being converted
            input_unicode++;
            
            if (w_char < 0x80) {
                // length = 1;
                utf8[index_buffer++] = static_cast<char>(w_char);
            } else if (w_char < 0x800) {
                // length = 2;
                utf8[index_buffer++] = 0xc0 | (w_char >> 6);
                utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
            } else if (w_char < 0x10000) {
                // length = 3;
                utf8[index_buffer++] = 0xe0 | (w_char >> 12);
                utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
                utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
            } else if (w_char < 0x200000) {
                // length = 4;
                utf8[index_buffer++] = 0xf0 | (static_cast<int>(w_char) >> 18);
                utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
                utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
            } else if (w_char < 0x4000000) {
                // length = 5
                utf8[index_buffer++] = 0xf8| (static_cast<int>(w_char) >> 24);
                utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 18) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
                utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
            } else {  // if(wchar >= 0x4000000)
                // all other cases length = 6
                utf8[index_buffer++] = 0xfc | (static_cast<int>(w_char) >> 30);
                utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 24) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 18) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
                utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
                utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
            }
        }
        while (w_char !=  static_cast<char>(0));
        
        // set the output length
        *length = buffer_size - 1;  // ignore last 
        
        // set the output charset
        *p_output_utf8 = utf8;
        return false;
    }
    
    
    
    bool UTF162UTF8(const UTF16 *input_unicode,
                    char ** p_output_utf8,
                    unsigned long *length) {
			if (input_unicode == NULL) {
				return true;
			}
			int size_d = 8;
			int buffer_size = 0;

			const UTF16* p_unicode = input_unicode;
			// count for the space need to allocate
			UTF16 w_char;
			do {
				w_char = *p_unicode;
				if (w_char < 0x80) {
					// utf char size is 1
					buffer_size += 1;
				} else if (w_char < 0x800) {
					// utf char size is 2
					buffer_size += 2;
				} else if (w_char < 0x10000) {
					// utf char size is 3
					buffer_size += 3;
				} else if (w_char < 0x200000) {
					// utf char size is 4
					buffer_size += 4;
				} else if (w_char < 0x4000000) {
					// utf char size is 5
					buffer_size += 5;
				} else {
					// utf char size is 6
					buffer_size += 6;
				}
				p_unicode++;
			}
			while (w_char != static_cast<char>(0));
			// allocate the memory
			char* utf8 = new char[buffer_size];

			p_unicode = input_unicode;
			int index_buffer = 0;
			// do the conversion
			do {
				w_char = *input_unicode;  // the unicode char current being converted
				input_unicode++;

				if (w_char < 0x80) {
					// length = 1;
					utf8[index_buffer++] = static_cast<char>(w_char);
				} else if (w_char < 0x800) {
					// length = 2;
					utf8[index_buffer++] = 0xc0 | (w_char >> 6);
					utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
				} else if (w_char < 0x10000) {
					// length = 3;
					utf8[index_buffer++] = 0xe0 | (w_char >> 12);
					utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
					utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
				} else if (w_char < 0x200000) {
					// length = 4;
					utf8[index_buffer++] = 0xf0 | (static_cast<int>(w_char) >> 18);
					utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
					utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
				} else if (w_char < 0x4000000) {
					// length = 5
					utf8[index_buffer++] = 0xf8| (static_cast<int>(w_char) >> 24);
					utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 18) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
					utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
				} else {  // if(wchar >= 0x4000000)
					// all other cases length = 6
					utf8[index_buffer++] = 0xfc | (static_cast<int>(w_char) >> 30);
					utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 24) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((static_cast<int>(w_char) >> 18) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((w_char >> 12) & 0x3f);
					utf8[index_buffer++] = 0x80 | ((w_char >> 6) & 0x3f);
					utf8[index_buffer++] = 0x80 | (w_char & 0x3f);
				}
			}
			while (w_char !=  static_cast<char>(0));

			// set the output length
			*length = buffer_size - 1;  // ignore last 

			// set the output charset
			*p_output_utf8 = utf8;
			return false;
	}

	bool UTF82Unicode(const char *input_utf8,
		wchar_t ** p_output_unicode,
		unsigned long *length) {
			if (input_utf8 == NULL) {  // input wrong.
				return false;
			}

			const char* p_current_char = input_utf8;
			unsigned long unicode_length = 0;
			char current_char;
			// calculate the size to locate
			do {
				// get the begining char
				current_char = *p_current_char;

				if ((current_char  & 0x80) == 0) {
					// 0xxxxxxx
					p_current_char++;
				} else if ((current_char  & 0xe0) == 0xc0) {
					// < 110x-xxxx 10xx-xxxx
					p_current_char += 2;
				} else if ((current_char  & 0xf0) == 0xe0) {
					// < 1110-xxxx 10xx-xxxx 10xx-xxxx
					p_current_char += 3;
				} else if ((current_char  & 0xf8) == 0xf0) {
					// < 1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
					p_current_char += 4;

				} else if ((current_char & 0xfc) == 0xf8) {
					// 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
					p_current_char += 5;
				} else {
					// if((current_char & 0xfe) == 0xfc)
					// 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
					p_current_char += 6;
				}
				unicode_length++;
			}
			while (current_char != 0);

			wchar_t* des = new wchar_t[unicode_length];
			unsigned long unicode_index = 0;
			p_current_char = input_utf8;

			do {
				current_char = *p_current_char;

				if ((current_char & 0x80) == 0) {
					des[unicode_index] = p_current_char[0];

					p_current_char++;
				} else if ((current_char & 0xE0) == 0xC0) {
					// < 110x-xxxx 10xx-xxxx
					wchar_t &wide_char = des[unicode_index];
					wide_char  = (p_current_char[0] & 0x3F) << 6;
					wide_char |= (p_current_char[1] & 0x3F);

					p_current_char += 2;
				} else if ((current_char & 0xF0) == 0xE0) {
					// < 1110-xxxx 10xx-xxxx 10xx-xxxx
					wchar_t &wide_char = des[unicode_index];

					wide_char  = (p_current_char[0] & 0x1F) << 12;
					wide_char |= (p_current_char[1] & 0x3F) << 6;
					wide_char |= (p_current_char[2] & 0x3F);

					p_current_char += 3;
				} else if ((current_char & 0xF8) == 0xF0) {
					// < 1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
					wchar_t &wide_char = des[unicode_index];

					wide_char  = (p_current_char[0] & 0x0F) << 18;
					wide_char |= (p_current_char[1] & 0x3F) << 12;
					wide_char |= (p_current_char[2] & 0x3F) << 6;
					wide_char |= (p_current_char[3] & 0x3F);

					p_current_char += 4;
				} else if ((current_char & 0xfc) == 0xf8) {
					// 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
					wchar_t &wide_char = des[unicode_index];

					wide_char = (p_current_char[0] & 0x07) << 24;
					wide_char |= (p_current_char[1] & 0x3F) << 18;
					wide_char |= (p_current_char[2] & 0x3F) << 12;
					wide_char |= (p_current_char[3] & 0x3F) << 6;
					wide_char |= (p_current_char[4] & 0x3F);

					p_current_char += 5;
				} else {
					// if((*current_char & 0xfe) == 0xfc)
					// 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

					wchar_t &wide_char = des[unicode_index];

					wide_char = (p_current_char[0] & 0x03) << 30;
					wide_char |= (p_current_char[1] & 0x3F) << 24;
					wide_char |= (p_current_char[2] & 0x3F) << 18;
					wide_char |= (p_current_char[3] & 0x3F) << 12;
					wide_char |= (p_current_char[4] & 0x3F) << 6;
					wide_char |= (p_current_char[5] & 0x3F);
					p_current_char += 6;
				}
				unicode_index++;
			} while (current_char != 0);

			*p_output_unicode =  des;
			*length = unicode_length - 1;  // ignore the last 

			return true;
	}


	size_t unicode2utf8(char *strinput, int len, char *stroutput, int maxoutlen)
	{
		char *p = NULL;
		unsigned long len_o = 0;
		UTF162UTF8((const UTF16 *)strinput,&p,&len_o);
		if (len <= maxoutlen && p)
		{
			memcpy(stroutput,p,len_o);
			if (p) delete p;
			return len_o;
		}
		if (p) delete p;
		return 0;
	}

	size_t utf82unicode(char *strinput, int len, char *stroutput, int maxoutlen)
	{
		wchar_t *p = NULL;
		unsigned long len_o = 0;
		UTF82Unicode((const char *)strinput,&p,&len_o);
		if (len <= maxoutlen && p)
		{
			memcpy(stroutput,p,len_o*2);
			if (p) delete p;
			return len_o*2;
		}
		if (p) delete p;
		return 0;	
	}
	void string_utf82unicode(std::string &str)
	{
		int len;
		char *szBuf = new char[str.size() * 2 + 32];
		char *szBuf2 = new char[str.size() * 2 + 32];	
		
		memset(szBuf, 0, str.size() * 2 + 32);
		memset(szBuf2, 0, str.size() * 2 + 32);
		strcpy(szBuf,str.c_str());
		len = utf82unicode(szBuf,strlen(szBuf)+1,szBuf2,str.size() * 2 + 32);
		//szBuf2[len] = 0;
#ifdef WIN32
		
		USES_CONVERSION;
		str = W2A((WCHAR*)szBuf2);
		
#else 
		char* pszBuf = NULL;
		int nRetCode = WtoA( (wchar_t*)szBuf2, &pszBuf );
		if ( nRetCode != RTN_ERROR )
		{
			str = pszBuf;
			RELEASE_PTR( pszBuf );
		}
#endif
		delete []szBuf;
		delete []szBuf2;
	}
	void string_unicode2utf8(std::wstring str, std::string &dest)
	{
		int len;
		
#ifdef WIN32
		WCHAR *szBuf = new WCHAR[str.size() + 32];
#else
		wchar_t *szBuf = new wchar_t[str.size() * 2 + 32];
#endif
		
		char *szBuf2 = new char[str.size() * 2 + 32];
		memset(szBuf,0,str.size() * 2 + 64);
		memset(szBuf2,0,str.size() * 2 + 32);
		wcscpy(szBuf,str.c_str());
		len = unicode2utf8((char*)szBuf,wcslen(szBuf)+1,szBuf2,str.size() * 2 + 32);
		//szBuf2[len] = 0;
		dest = szBuf2;
		
		delete []szBuf;
		delete []szBuf2;
	}
}

long AssicToUTF8(char* pszAnsi, char** pszUTF8)
{
	int nRet = RTN_ERROR;
	unsigned long nLen = 0;

	if ( pszAnsi == NULL)
	{
		return nRet;
	}

	wchar_t* pszwStr = NULL;
	nLen = AtoW( pszAnsi, &pszwStr );
	if ( nLen == RTN_ERROR )
	{
		return nRet;		
	}
	
	Charset_Coders::Unicode2UTF8( pszwStr, pszUTF8, &nLen );
	RELEASE_PTR( pszwStr );
	
	return nLen;
}
